Analysis of Cyber-security Threats
Abstract
The purpose of this data analysis was to find the most efficient
model for predicting financial loss due to cyber security attacks. Cyber
security attacks effect a number of different types of industries and
can be harmful not only to the privacy of the company, but can also
contribute to company losses. This analysis will explore LASSO, Ridge
and Elastic Net linear regression to find an optimal model in predicting
financial loss. It will also explore support vector regression to fit
the data to a certain margin of error and minimize complexity of the
model.
Introduction
How much money can be lost in a single cyber security threat? By
modeling and observing trends in cyber security threat data, this
analysis seeks to help reduce the financial impact of cyber security
threats. Limitations of this analysis include under reporting and data
bias. Many organizations will not report security breaches publicly to
avoid damage to their reputation. As well, threats can evolve quickly,
leading to models becoming outdated rapidly.
Overview of Data
The dataset was obtained from Kaggle and contains 10 variables that
are potential significant covariates in predicting potential threats.
The variable country refers to the country where the attack occurred.
The other variables analyze incident resolution time in hours, number of
affected users, financial loss, attack type, target industry, attack
source, security vulnerability type and defense mechanism used. Here is
a full list of variables:
- Country country where the attack occurred
- Year year of the incident
- Attack Type type of cyber-security threat
- Target Industry industry targeted
- Financial Loss estimated financial loss in
millions
- Number of Affected Users number of users impacted
by the attack
- Attack Source origin of the attack
- Security Vulnerability Type type of vulnerability
exploited
- Defense Mechanism Used cyber-security defense
strategy applied
- Incident Resolution Time time taken to fully
resolve event in hours
cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')
cyber$Defense.Mechanism.Used <- ifelse(cyber$Defense.Mechanism.Used == "AI-based Detection", "AI", cyber$Defense.Mechanism.Used)
# plot 1 number of affected users by financial loss by vulnerability type
ggplot(cyber, aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Number.of.Affected.Users, y = cyber$Financial.Loss..in.Million..., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
scale_size(range = c(2, 10)) +
labs(
title = "Plot of Security Vulnerability, Volume Compromised, and Financial Impact",
x = "Volume Compromised",
y = "Financial Impact",
color = "Security Vulernability"
) +
theme_minimal()
# plot 3 Density plot of attacked industries
Year_data <- cyber %>%
filter(Year == 2015)
p1<- ggplot(Year_data, aes(x = Number.of.Affected.Users, fill = Country)) +
geom_density(alpha = 0.6) +
labs(
x = "Number of Affected Users",
y = "Density",
fill="Country",
title = "Density Plot of Attacked Countries in 2015"
) +
theme_minimal()
ggplotly(p1)
Year_data1 <- cyber %>%
filter(Year == 2024)
# plot 4 Density plot of Attacked countries
p2 <- ggplot(Year_data1, aes(x = Number.of.Affected.Users, fill = Country)) +
geom_density(alpha = 0.6) +
labs(
x = "Number of Affected Users",
y = "Density",
fill="Country",
title = "Density Plot of Attacked Countries in 2024"
) +
theme_minimal()
ggplotly(p2)
Each country is given a specific color and the number of affected
users is displayed on the x-axis. The density is displayed on the
y-axis. Density in this plot shows the relative frequency of number of
affected users per attack in each country.In 2015, the cyber attacks
within the USA more frequently affected about 310,000 users, compared to
Russia where attacks more frequently attacked 670,000 users and Germany
where most attacks more frequently affected 790,000 users. In 2024, the
number of affected users in the USA does not seem to be much more dense
in any one number and is more dispersed. Japan had more frequent attacks
that affected 500,000 and Brazil had it’s most frequent at 750,000
users.
ggplot(cyber, aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type)) +
geom_smooth(aes(x = cyber$Year, y = cyber$Incident.Resolution.Time..in.Hours., color = cyber$Security.Vulnerability.Type), method = "lm", se = FALSE) +
scale_size(range = c(2, 10)) +
labs(
title = "Plot of Security Vulnerability, Year of Attack, and Resolution Time",
x = "Year of Attack",
y = "Incident Resolution Time in Hours",
color = "Security Vulernability"
) +
theme_minimal()
#plot of zero-day attack type by target industry
zero_data <- cyber %>%
filter(Security.Vulnerability.Type == "Zero-day")
a <- ggplot(zero_data, aes(x = zero_data$Attack.Type, fill =zero_data$Target.Industry )) +
geom_bar(position = "dodge") +
labs(
x = "Threat Type",
y = "Proportion",
fill = "Target Industry",
title = "Zero-day Incidences by Threat Type vs. Target Industry"
) +
theme_minimal()
ggplotly(a)
Zero-day attacks seem to commonly use Phising within both retail and
IT industries, based on the height and count of the bar in these two
categories. Ransomware is the most minimally used zero-day attack on the
government, with a count of just 12.
col0=c("#4682B4", "#B4464B","#9900FF","#FF66FF", "#009933")
boxplot(Incident.Resolution.Time..in.Hours. ~ Defense.Mechanism.Used,
data=cyber,
xlab="Defense Mechanism",
ylab="Resolution Time (hours)",
col = col0,
main="Incident Resolution Time by Defense Mechanism",
cex.main = 0.9,
col.main = "blue")
The boxplot shows that the average resolution time among attacks
based on defense mechanisms is relatively the same. The only exception
seems to be firewall, which appears to take slightly less resolution
time on average.We can indicate the average by looking the black
horizontal line within each box. They all appear to line up with one
another indicating similar averages.
Skewness
cyber$Country[cyber$Country=="USA"]<-1
cyber$Country<- ifelse(cyber$Country!="USA",0)
cyber$Security.Vulnerability.Type[cyber$Security.Vulnerability.Type=="Zero-day"]<-1
cyber$Security.Vulnerability.Type<-ifelse(cyber$Security.Vulnerability.Type!="Zero-day",0)
cyber$Number.of.Affected.Users<-as.numeric(cyber$Number.of.Affected.Users)
cyber$Year<-as.numeric(as.factor(cyber$Year))
cyber$Incident.Resolution.Time..in.Hours.<-as.numeric(cyber$Incident.Resolution.Time..in.Hours.)
c<-skewness(cyber$Financial.Loss..in.Million...)
d<-skewness(cyber$Number.of.Affected.Users)
e<-skewness(cyber$Year)
Skewness = cbind(Financial.Loss = c,
Affected.Users = d,
Year = e)
pander(Skewness)
| -0.01684 |
-0.02537 |
-0.02749 |
The data is normally skewed and no transformation is necessary.
Regularized Regression Modeling
For this analysis, we will explore three types of regularized linear
regression modeling to find key predictors for financial loss from cyber
security attacks.
# Predictors (x) and Response Variable (y)
set.seed(12345)
X <- as.data.frame(cyber[, -5])
y <- cyber$Financial.Loss..in.Million...
train_index <- createDataPartition(y, p = 0.8, list = FALSE)
X_train <- makeX(X[train_index, ])
X_test <- makeX(X[-train_index, ])
y_train <- y[train_index]
y_test <- y[-train_index]
#standardizing the data
preprocess_params <- preProcess(X_train, method = c("center","scale"))
X_train <- predict(preprocess_params, X_train)
X_test <- predict(preprocess_params, X_test)
#fitting the model
fit_lasso <- glmnet(X_train, y_train, alpha = 1) # Lasso
fit_ridge <- glmnet(X_train, y_train, alpha = 0) # Ridge
fit_elastic_net <- glmnet(X_train, y_train, alpha = 0.5) # Elastic Net
#cross-validation
cv_lasso <- cv.glmnet(X_train, y_train, alpha = 1) # Lasso CV
cv_ridge <- cv.glmnet(X_train, y_train, alpha = 0) # Ridge CV
cv_elastic_net <- cv.glmnet(X_train, y_train, alpha = 0.5) # Elastic Net CV
#plot coefficient path
par(mar=c(5,4,6,3))
# Plot coefficient path
plot(fit_lasso, xvar = "lambda", label = TRUE,
lwd = 1.5,
main = "Coefficient Path Analysis: LASSO",
cex.main = 0.9,
col = rainbow(10))
abline(v = log(1), col = "purple", lty = 4, lwd = 2)
abline(v = -1, col = "steelblue", lty = 2, lwd = 2)
pander(data.frame(colnames(X_train)[c(27, 5, 20, 12)]),caption= "Feature Variables at log(Lambda) = 0")
Feature Variables at log(Lambda) = 0
| Incident.Resolution.Time..in.Hours. |
| Attack.TypeMan-in-the-Middle |
| Attack.SourceUnknown |
| Target.IndustryHealthcare |
pander(data.frame(colnames(X_train)[c(18, 12, 10, 24, 5, 23, 17, 11, 3)]),caption= " Feature Variables at log(Lambda) = -1")
Feature Variables at log(Lambda) = -1
| Attack.SourceInsider |
| Target.IndustryHealthcare |
| Target.IndustryEducation |
| Defense.Mechanism.UsedEncryption |
| Attack.TypeMan-in-the-Middle |
| Defense.Mechanism.UsedAntivirus |
| Attack.SourceHacker Group |
| Target.IndustryGovernment |
| Attack.TypeDDoS |
The table above displays the feature variables observed in the
Coefficient Analysis plot.
par(mar=c(5,4,6,3))
##
plot(cv_lasso, main = "RMSE Plot: LASSO",
cex.main = 0.9)
# Extract coefficients for the best lambda
best.lasso.lambda <- cv_lasso$lambda.min
best.ridge.lambda <- cv_ridge$lambda.min
best.elastic.net.lambda <- cv_elastic_net$lambda.min
##
# Lasso Regression (L1 Regularization):
# CAUTION: model formula differs from the regular regression formula
lasso_model.opt <- glmnet(X_train,
y_train,
alpha = 1, # lasso regression
lambda = best.lasso.lambda,standardize = TRUE) # useser selected alpha, optimal lambda
# can be obtained through CV (see below)
lasso_predictions.opt <- predict(lasso_model.opt,
s = best.lasso.lambda, # user selected lambda value
# (regularization paremeter)
newx = X_test) # test data set
# The following RMSE of prediction serves as a validation - one step validation
lasso_rmse.opt <- sqrt(mean((y_test - lasso_predictions.opt)^2))
# Ridge Regression (L2 Regularization)
ridge_model.opt <- glmnet(X_train, y_train, alpha = 0, lambda = best.ridge.lambda,standardize=TRUE)
ridge_predictions.opt <- predict(ridge_model.opt, s = best.ridge.lambda, newx = X_test)
ridge_rmse.opt <- sqrt(mean((y_test - ridge_predictions.opt)^2))
# Elastic Net (Combination of L1 and L2)
elastic_net_model.opt <- glmnet(X_train, y_train, alpha = 0.5, lambda = 0.1,standardize = TRUE)
elastic_net_predictions.opt <- predict(elastic_net_model.opt, s = 0.1, newx = X_test)
elastic_net_rmse.opt <- sqrt(mean((y_test - elastic_net_predictions.opt)^2))
RMSE.opt = cbind(LASSO.opt = lasso_rmse.opt,
Ridge.opt = ridge_rmse.opt,
Elasticnet.opt = elastic_net_rmse.opt)
pander(RMSE.opt)
The optimal lambda for each type of linear regression is shown
above.
LASSO Regression Equation
Now, a final model will be extracted using LASSO, Ridge, and Elastic
Net.
#Lasso
# Extract coefficients with names from glmnet
coef_lasso <- coef(cv_lasso, s = best.lasso.lambda)
coef_df <- as.data.frame(as.matrix(coef_lasso))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.6683*Attack.TypeDDoS + -0.4171*Target.IndustryEducation + 0.6626*Target.IndustryGovernment + -0.3464*Target.IndustryHealthcare + 0.265*Attack.SourceHacker Group + -0.5335*Attack.SourceInsider + 0.0065*Defense.Mechanism.UsedAntivirus
Ridge Regression Equation
# Extract coefficients with names from glmnet
coef_ridge <- coef(cv_ridge, s = best.ridge.lambda)
coef_df <- as.data.frame(as.matrix(coef_ridge))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.0114*Year + 0.0521*Attack.TypeDDoS + -0.0125*Attack.TypeMalware + 0.0145*Attack.TypeMan-in-the-Middle + -0.0171*Attack.TypePhishing + -0.0201*Attack.TypeRansomware + -0.0186*Attack.TypeSQL Injection + 7e-04*Target.IndustryBanking + -0.0445*Target.IndustryEducation + 0.0576*Target.IndustryGovernment + -0.0398*Target.IndustryHealthcare + 0.0036*Target.IndustryIT + 0.0055*Target.IndustryRetail + 0.0187*Target.IndustryTelecommunications + 6e-04*Number.of.Affected.Users + 0.0427*Attack.SourceHacker Group + -0.0496*Attack.SourceInsider + 0.0182*Attack.SourceNation-state + -0.0106*Attack.SourceUnknown + -0.0045*Defense.Mechanism.UsedAI + 0.0233*Defense.Mechanism.UsedAntivirus + -0.0193*Defense.Mechanism.UsedEncryption + 0.0027*Defense.Mechanism.UsedFirewall + -0.0029*Defense.Mechanism.UsedVPN + -0.0122*Incident.Resolution.Time..in.Hours.
Elastic Net Regression Equation
# Extract coefficients with names from glmnet
coef_elastic <- coef(cv_elastic_net, s = best.elastic.net.lambda)
coef_df <- as.data.frame(as.matrix(coef_elastic))
coef_df$feature <- rownames(coef_df)
colnames(coef_df)[1] <- "coefficient"
# Filter non-zero coefficients
nonzero_coefs <- subset(coef_df, coefficient != 0)
# Separate intercept
intercept <- round(nonzero_coefs$coefficient[nonzero_coefs$feature == "(Intercept)"], 4)
nonzero_terms <- subset(nonzero_coefs, feature != "(Intercept)")
# Build model equation string
equation <- paste0(round(nonzero_terms$coefficient, 4), "*", nonzero_terms$feature)
cat("Model equation: y =", intercept, "+", paste(equation, collapse = " + "), "\n")
Model equation: y = 50.5369 + 0.4941*Attack.TypeDDoS + -0.2438*Target.IndustryEducation + 0.5433*Target.IndustryGovernment + -0.168*Target.IndustryHealthcare + 0.1465*Attack.SourceHacker Group + -0.3984*Attack.SourceInsider
lasso.error <- min(cv_lasso$cvm)
ridge.error <- min(cv_ridge$cvm)
enet.error <- min(cv_elastic_net$cvm)
model.errors <- data.frame(
Model = c("LASSO", "Ridge", "Elastic Net"),
CV_Error = c(lasso.error, ridge.error, enet.error)
)
pander(model.errors)
| LASSO |
829.7 |
| Ridge |
829.9 |
| Elastic Net |
830.3 |
Conclusion for Regularized Regression Modeling
The table above shows the MSE values or cross-validated errors for
our three models. A smaller value indicates a model with greater
performance. The LASSO linear regression model out performs both the
Ridge and Elastic Net model. When predicting financial loss caused by
cyber security threats, the LASSO liner regression model will be the
most efficient.
$$
= 50.5369 + 4.818 - 0.4171 + 2.324 - 0.3464 + 0.265 - 0.5335 +
0.0065
$$
Support Vector Regression
For this section of the analysis, linear and radial basis functions
will be fit using support vector regression and an ordinary least
squares regression model will be fit using step-wise variable
selection.
cyber <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/Global_Cybersecurity_Threats_2015-2024.csv')
#One-Hot Encoding
dummy <- dummyVars("~.", data=cyber)
cyber2<-data.frame(predict(dummy, newdata=cyber))
#####
# Split data into features (X) and target (y)
X <- cyber2[, -25] # All columns except the target variable
y <- cyber2[, 25] # Target variable (Financial Loss in Millions)
# create dummy variables
#####
# Split data into training and testing sets
set.seed(123)
train.index <- sample(1:nrow(cyber2), 0.8 * nrow(cyber2))
X.train <- X[train.index, ]
y.train <- y[train.index]
X.test <- X[-train.index, ]
y.test <- y[-train.index]
#####
cyber2.train <- as.data.frame(cyber2[train.index, ])
cyber2.test <- cyber2[-train.index, ]
#####
## Set up custom cross-validation control
my_tune_control <- tune.control(
cross = 5, # Use 5-fold cross-validation, the default is 10-fold cross-validation
nrepeat = 1 # Number of repetitions (for repeated cross-validation)
)
#####
# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.RBF <- tune(svm,
train.x = X.train,
train.y = y.train,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100),
gamma = c(0.01, 0.1, 1)), # Hyperpar in RBF
tunecontrol = my_tune_control # 5-fold cross-validation
)
####
# Display the best parameters
#print(tune.RBF$best.parameters)
#####
# Train the final model using the best parameters
final.RBF<- svm(X.train, y.train,
type = "eps-regression", # Use "nu-regression" for nu-SVR
kernel = "radial",
epsilon = tune.RBF$best.parameters$epsilon,
cost = tune.RBF$best.parameters$cost,
gamma = tune.RBF$best.parameters$gamma)
#####
# Make predictions on the test set
pred.RBF <- predict(final.RBF, X.test)
# Evaluate performance
mse.RBF <- mean((y.test - pred.RBF)^2) # mean square error
mae.RBF <- mean(abs(y.test - pred.RBF)) # mean absolute error
#### linear kernel
# Perform grid search for hyperparameter tuning: RBF kernel is used
tune.lin <- tune(svm, train.x = X.train, train.y = y.train,
ranges = list(epsilon = seq(0.1, 0.5, 0.1),
cost = c(1, 10, 100)),
tunecontrol = my_tune_control # 5-fold cross-validation
)
####
# Display the best parameters
#print(tune.lin$best.parameters)
#####
# Train the final model using the best parameters
final.lin<- svm(X.train, y.train,
type = "eps-regression", # Use "nu-regression" for nu-SVR
kernel = "linear",
epsilon = tune.lin$best.parameters$epsilon,
cost = tune.lin$best.parameters$cost)
#####
# Make predictions on the test set
pred.lin <- predict(final.lin, X.test)
# Evaluate performance
mse.lin <- mean((y.test - pred.lin)^2) # mean square error
mae.lin <- mean(abs(y.test - pred.lin))
RBF <- tune.RBF$best.parameters
Linear <- tune.lin$best.parameters
pander(RBF, caption='RBF Best Parameters')
RBF Best Parameters
| 35 |
0.5 |
1 |
1 |
pander(Linear, caption='Linear SVR Best Parameters')
Linear SVR Best Parameters
| 5 |
0.5 |
1 |
The tables above show the fine tuned best parameters for both support
vector regression models.
## ordinary LSE regression model with stepwise variable selection
lse.fit <- lm(Financial.Loss..in.Million...~.,data=cyber2.train)
AIC.fit <- stepAIC(lse.fit,direction="both", trace = FALSE)
pred.lse <- predict(AIC.fit, X.test)
mse.lse <- mean((y.test - pred.lse)^2) # mean square error
mae.lse <- mean(abs(y.test - pred.lse)) # mean absolute error
###
par(mfrow=c(2,2), mar=c(2,2,2,2))
plot(AIC.fit)
The model above satisfies the linear regression assumptions and will
be used to help compare performance.
Comparing Regression Models
In this section we will look at the performance of our least squares
regression model after stepwise variable selection with our linear and
radial basis function models from support vector regression.We will
compare the model using MSE and MAE values.
###
Performance <- data.frame(RBF.SVR=c(mse.RBF, mae.RBF),
Linear.SVR = c(mse.lin, mae.lin),
LSE.Reg =c(mse.lse, mae.lse))
row.names(Performance) <- c("MSE", "MAE")
##
pander(Performance)
| MSE |
811.8 |
829.2 |
810.4 |
| MAE |
24.54 |
24.75 |
24.45 |
The linear and least squares model performed better than the radial
basis function model. The two models performed similarly, but the least
squares model performed slightly better and will be chosen as the best
model for the data.
Conclusion for Support Vector Regression
Based on our MSE and MAE scores, our least squares estimation has the
best model based on performance in comparison to support vector
regression on linear and radial basis function. It has lower MSE and MAE
scores indicating a better fit model. Our final best model is:
\[
\text{Financial Loss in Millions} = 51.27 + 4.818\times
\text{CountryGermany} - 3.525\times \text{CountryIndia} - 2.324\times
\text{AttackSourceInsider}
\]
LS0tCnRpdGxlOiAiUmVndWxhcml6ZWQgUmVncmVzc2lvbiBNb2RlbGluZyBvbiBGaW5hbmNpYWwgTG9zcyBkdWUgdG8gQ3liZXIgVGhyZWF0cyIKYXV0aG9yOiAiR2FicmllbGxhIEtoYWxpbCIKZGF0ZTogIjIwMjUtMDMtMjYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgo8L3N0eWxlPgpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCn0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQpsaWJyYXJ5KEdHYWxseSkKfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhbmRlcikKbGlicmFyeShtb21lbnRzKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKIyBHZW5lcmFsCmxpYnJhcnkoZTEwNzEpICAgICAgICAjIEZvciBTVk0gICAgICAgIyBGb3IgUmlkZ2UsIExhc3NvLCBsb2dpc3RpYyByZWdyZXNzaW9uCmxpYnJhcnkocFJPQykgICAgICAgICAjIEZvciBST0MgY3VydmVzCmxpYnJhcnkoTWV0cmljcykgICAgICAjIEZvciBNU0UvTUFFCmxpYnJhcnkoTUFTUykKIyBPcHRpb25hbCB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ3JpZEV4dHJhKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAoKIyBBbmFseXNpcyBvZiBDeWJlci1zZWN1cml0eSBUaHJlYXRzCgojIyBBYnN0cmFjdAoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBkYXRhIGFuYWx5c2lzIHdhcyB0byBmaW5kIHRoZSBtb3N0IGVmZmljaWVudCBtb2RlbCBmb3IgcHJlZGljdGluZyBmaW5hbmNpYWwgbG9zcyBkdWUgdG8gY3liZXIgc2VjdXJpdHkgYXR0YWNrcy4gQ3liZXIgc2VjdXJpdHkgYXR0YWNrcyBlZmZlY3QgYSBudW1iZXIgb2YgZGlmZmVyZW50IHR5cGVzIG9mIGluZHVzdHJpZXMgYW5kIGNhbiBiZSBoYXJtZnVsIG5vdCBvbmx5IHRvIHRoZSBwcml2YWN5IG9mIHRoZSBjb21wYW55LCBidXQgY2FuIGFsc28gY29udHJpYnV0ZSB0byBjb21wYW55IGxvc3Nlcy4gVGhpcyBhbmFseXNpcyB3aWxsIGV4cGxvcmUgTEFTU08sIFJpZGdlIGFuZCBFbGFzdGljIE5ldCBsaW5lYXIgcmVncmVzc2lvbiB0byBmaW5kIGFuIG9wdGltYWwgbW9kZWwgaW4gcHJlZGljdGluZyBmaW5hbmNpYWwgbG9zcy4gSXQgd2lsbCBhbHNvIGV4cGxvcmUgc3VwcG9ydCB2ZWN0b3IgcmVncmVzc2lvbiB0byBmaXQgdGhlIGRhdGEgdG8gYSBjZXJ0YWluIG1hcmdpbiBvZiBlcnJvciBhbmQgbWluaW1pemUgY29tcGxleGl0eSBvZiB0aGUgbW9kZWwuCgojIyBJbnRyb2R1Y3Rpb24KCkhvdyBtdWNoIG1vbmV5IGNhbiBiZSBsb3N0IGluIGEgc2luZ2xlIGN5YmVyIHNlY3VyaXR5IHRocmVhdD8gQnkgbW9kZWxpbmcgYW5kIG9ic2VydmluZyB0cmVuZHMgaW4gY3liZXIgc2VjdXJpdHkgdGhyZWF0IGRhdGEsIHRoaXMgYW5hbHlzaXMgc2Vla3MgdG8gaGVscCByZWR1Y2UgdGhlIGZpbmFuY2lhbCBpbXBhY3Qgb2YgY3liZXIgc2VjdXJpdHkgdGhyZWF0cy4gTGltaXRhdGlvbnMgb2YgdGhpcyBhbmFseXNpcyBpbmNsdWRlIHVuZGVyIHJlcG9ydGluZyBhbmQgZGF0YSBiaWFzLiBNYW55IG9yZ2FuaXphdGlvbnMgd2lsbCBub3QgcmVwb3J0IHNlY3VyaXR5IGJyZWFjaGVzIHB1YmxpY2x5IHRvIGF2b2lkIGRhbWFnZSB0byB0aGVpciByZXB1dGF0aW9uLiBBcyB3ZWxsLCB0aHJlYXRzIGNhbiBldm9sdmUgcXVpY2tseSwgbGVhZGluZyB0byBtb2RlbHMgYmVjb21pbmcgb3V0ZGF0ZWQgcmFwaWRseS4KCiMjIE92ZXJ2aWV3IG9mIERhdGEKClRoZSBkYXRhc2V0IHdhcyBvYnRhaW5lZCBmcm9tIEthZ2dsZSBhbmQgY29udGFpbnMgMTAgdmFyaWFibGVzIHRoYXQgYXJlIHBvdGVudGlhbCBzaWduaWZpY2FudCBjb3ZhcmlhdGVzIGluIHByZWRpY3RpbmcgcG90ZW50aWFsIHRocmVhdHMuIFRoZSB2YXJpYWJsZSBjb3VudHJ5IHJlZmVycyB0byB0aGUgY291bnRyeSB3aGVyZSB0aGUgYXR0YWNrIG9jY3VycmVkLiBUaGUgb3RoZXIgdmFyaWFibGVzIGFuYWx5emUgaW5jaWRlbnQgcmVzb2x1dGlvbiB0aW1lIGluIGhvdXJzLCBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMsIGZpbmFuY2lhbCBsb3NzLCBhdHRhY2sgdHlwZSwgdGFyZ2V0IGluZHVzdHJ5LCBhdHRhY2sgc291cmNlLCBzZWN1cml0eSB2dWxuZXJhYmlsaXR5IHR5cGUgYW5kIGRlZmVuc2UgbWVjaGFuaXNtIHVzZWQuIEhlcmUgaXMgYSBmdWxsIGxpc3Qgb2YgdmFyaWFibGVzOgoKLSAgICoqQ291bnRyeSoqIGNvdW50cnkgd2hlcmUgdGhlIGF0dGFjayBvY2N1cnJlZAotICAgKipZZWFyKiogeWVhciBvZiB0aGUgaW5jaWRlbnQKLSAgICoqQXR0YWNrIFR5cGUqKiB0eXBlIG9mIGN5YmVyLXNlY3VyaXR5IHRocmVhdAotICAgKipUYXJnZXQgSW5kdXN0cnkqKiBpbmR1c3RyeSB0YXJnZXRlZAotICAgKipGaW5hbmNpYWwgTG9zcyoqIGVzdGltYXRlZCBmaW5hbmNpYWwgbG9zcyBpbiBtaWxsaW9ucwotICAgKipOdW1iZXIgb2YgQWZmZWN0ZWQgVXNlcnMqKiBudW1iZXIgb2YgdXNlcnMgaW1wYWN0ZWQgYnkgdGhlIGF0dGFjawotICAgKipBdHRhY2sgU291cmNlKiogb3JpZ2luIG9mIHRoZSBhdHRhY2sKLSAgICoqU2VjdXJpdHkgVnVsbmVyYWJpbGl0eSBUeXBlKiogdHlwZSBvZiB2dWxuZXJhYmlsaXR5IGV4cGxvaXRlZAotICAgKipEZWZlbnNlIE1lY2hhbmlzbSBVc2VkKiogY3liZXItc2VjdXJpdHkgZGVmZW5zZSBzdHJhdGVneSBhcHBsaWVkCi0gICAqKkluY2lkZW50IFJlc29sdXRpb24gVGltZSoqIHRpbWUgdGFrZW4gdG8gZnVsbHkgcmVzb2x2ZSBldmVudCBpbiBob3VycwoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGlzIHBsb3Qgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNlY3VyaXR5IHZ1bG5lcmFiaWxpdHkgdHlwZSBhbmQgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdGhlIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBhbmQgZmluYW5jaWFsIGxvc3MgaW4gbWlsbGlvbnMuIEluIG1vc3QgY2FzZXMgdGhlIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBzZWVtcyB0byBiZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBmaW5hbmNpYWwgbG9zcy4gVGhlIG9ubHkgZXhjZXB0aW9uIGlzIHNlZW4gaW4gemVyby1kYXkuIHplcm8tZGF5IHJlZmVycyB0byBhIHR5cGUgb2YgdnVsbmVyYWJpbGl0eSB3aGVyZSB0aGUgdGFyZ2V0IG9mIHRoZSBhdHRhY2sgaXMgdW5hd2FyZSBvZiB0aGUgYXR0YWNrLiBNZWFuaW5nIHRoZXJlIGFyZSB6ZXJvIGRheXMgZm9yIHRoZSBwcm9ibGVtIHRvIGJlIHNvbHZlZCBiZWZvcmUgaXQgaGFzIGJlZW4gZXhwbG9pdGVkLiBJdCB3b3VsZCBtYWtlIHNlbnNlIHRoYXQgaW4gdGhpcyB0eXBlIG9mIHZ1bG5lcmFiaWxpdHksIHRoZSBmZXdlciB1c2VycyBhZmZlY3RlZCB3b3VsZCBsZWFkIHRvIGEgZ3JlYXRlciBmaW5hbmNpYWwgbG9zcy4nfQpjeWJlciA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dhYmJ5SzgvRGF0YXNldHMvcmVmcy9oZWFkcy9tYWluL0dsb2JhbF9DeWJlcnNlY3VyaXR5X1RocmVhdHNfMjAxNS0yMDI0LmNzdicpCmN5YmVyJERlZmVuc2UuTWVjaGFuaXNtLlVzZWQgPC0gaWZlbHNlKGN5YmVyJERlZmVuc2UuTWVjaGFuaXNtLlVzZWQgPT0gIkFJLWJhc2VkIERldGVjdGlvbiIsICJBSSIsIGN5YmVyJERlZmVuc2UuTWVjaGFuaXNtLlVzZWQpCgojIHBsb3QgMSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgYnkgZmluYW5jaWFsIGxvc3MgYnkgdnVsbmVyYWJpbGl0eSB0eXBlCgoKZ2dwbG90KGN5YmVyLCBhZXMoeCA9IGN5YmVyJE51bWJlci5vZi5BZmZlY3RlZC5Vc2VycywgeSA9IGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uLCBjb2xvciA9IGN5YmVyJFNlY3VyaXR5LlZ1bG5lcmFiaWxpdHkuVHlwZSkpICsKZ2VvbV9zbW9vdGgoYWVzKHggPSBjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIHkgPSBjeWJlciRGaW5hbmNpYWwuTG9zcy4uaW4uTWlsbGlvbi4uLiwgY29sb3IgPSBjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUpLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMiwgMTApKSArCiAgbGFicygKICAgIHRpdGxlID0gIlBsb3Qgb2YgU2VjdXJpdHkgVnVsbmVyYWJpbGl0eSwgVm9sdW1lIENvbXByb21pc2VkLCBhbmQgRmluYW5jaWFsIEltcGFjdCIsCiAgICB4ID0gIlZvbHVtZSBDb21wcm9taXNlZCIsCiAgICB5ID0gIkZpbmFuY2lhbCBJbXBhY3QiLAogICAgY29sb3IgPSAiU2VjdXJpdHkgVnVsZXJuYWJpbGl0eSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoaXMgcGxvdCBzaG93cyB0aGUgZGVuc2l0eSBvZiBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgaW4gY3liZXJzZWN1cml0eSBhdHRhY2tzIGluIGVhY2ggY291bnRyeSBpbiB0aGUgeWVhciAyMDE1LiAnfQojIHBsb3QgMyBEZW5zaXR5IHBsb3Qgb2YgYXR0YWNrZWQgaW5kdXN0cmllcwpZZWFyX2RhdGEgPC0gY3liZXIgJT4lCiAgZmlsdGVyKFllYXIgPT0gMjAxNSkKcDE8LSBnZ3Bsb3QoWWVhcl9kYXRhLCBhZXMoeCA9IE51bWJlci5vZi5BZmZlY3RlZC5Vc2VycywgZmlsbCA9IENvdW50cnkpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC42KSArCiAgbGFicygKICAgIHggPSAiTnVtYmVyIG9mIEFmZmVjdGVkIFVzZXJzIiwKICAgIHkgPSAiRGVuc2l0eSIsCiAgICBmaWxsPSJDb3VudHJ5IiwKICAgIHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBBdHRhY2tlZCBDb3VudHJpZXMgaW4gMjAxNSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKZ2dwbG90bHkocDEpCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSJUaGlzIHBsb3Qgc2hvd3MgdGhlIGRlbnNpdHkgb2YgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIGluIGN5YmVyc2VjdXJpdHkgYXR0YWNrcyBpbiBlYWNoIGNvdW50cnkgaW4gdGhlIHllYXIgMjAyNC4gIn0KIFllYXJfZGF0YTEgPC0gY3liZXIgJT4lCiAgZmlsdGVyKFllYXIgPT0gMjAyNCkKIyBwbG90IDQgRGVuc2l0eSBwbG90IG9mIEF0dGFja2VkIGNvdW50cmllcwpwMiA8LSBnZ3Bsb3QoWWVhcl9kYXRhMSwgYWVzKHggPSBOdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMsIGZpbGwgPSBDb3VudHJ5KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNikgKwogIGxhYnMoCiAgICB4ID0gIk51bWJlciBvZiBBZmZlY3RlZCBVc2VycyIsCiAgICB5ID0gIkRlbnNpdHkiLAogICAgZmlsbD0iQ291bnRyeSIsCiAgICB0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgQXR0YWNrZWQgQ291bnRyaWVzIGluIDIwMjQiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KHAyKQoKYGBgCgpFYWNoIGNvdW50cnkgaXMgZ2l2ZW4gYSBzcGVjaWZpYyBjb2xvciBhbmQgdGhlIG51bWJlciBvZiBhZmZlY3RlZCB1c2VycyBpcyBkaXNwbGF5ZWQgb24gdGhlIHgtYXhpcy4gVGhlIGRlbnNpdHkgaXMgZGlzcGxheWVkIG9uIHRoZSB5LWF4aXMuIERlbnNpdHkgaW4gdGhpcyBwbG90IHNob3dzIHRoZSByZWxhdGl2ZSBmcmVxdWVuY3kgb2YgbnVtYmVyIG9mIGFmZmVjdGVkIHVzZXJzIHBlciBhdHRhY2sgaW4gZWFjaCBjb3VudHJ5LkluIDIwMTUsIHRoZSBjeWJlciBhdHRhY2tzIHdpdGhpbiB0aGUgVVNBIG1vcmUgZnJlcXVlbnRseSBhZmZlY3RlZCBhYm91dCAzMTAsMDAwIHVzZXJzLCBjb21wYXJlZCB0byBSdXNzaWEgd2hlcmUgYXR0YWNrcyBtb3JlIGZyZXF1ZW50bHkgYXR0YWNrZWQgNjcwLDAwMCB1c2VycyBhbmQgR2VybWFueSB3aGVyZSBtb3N0IGF0dGFja3MgbW9yZSBmcmVxdWVudGx5IGFmZmVjdGVkIDc5MCwwMDAgdXNlcnMuIEluIDIwMjQsIHRoZSBudW1iZXIgb2YgYWZmZWN0ZWQgdXNlcnMgaW4gdGhlIFVTQSBkb2VzIG5vdCBzZWVtIHRvIGJlIG11Y2ggbW9yZSBkZW5zZSBpbiBhbnkgb25lIG51bWJlciBhbmQgaXMgbW9yZSBkaXNwZXJzZWQuIEphcGFuIGhhZCBtb3JlIGZyZXF1ZW50IGF0dGFja3MgdGhhdCBhZmZlY3RlZCA1MDAsMDAwIGFuZCBCcmF6aWwgaGFkIGl0J3MgbW9zdCBmcmVxdWVudCBhdCA3NTAsMDAwIHVzZXJzLgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGlzIHBsb3Qgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNlY3VyaXR5IHZ1bG5lcmFiaWxpdHkgdHlwZSBhbmQgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gdGhlIHllYXIgb2YgYXR0YWNrIGFuZCByZXNvbHV0aW9uIHRpbWUuIFRoZSBwbG90IHNob3dzIGhvdyBvdmVyIHRoZSB5ZWFycyByZXNvbHV0aW9uIHRpbWUgaGFzIGltcHJvdmVkIGZvciBjZXJ0YWluIHZ1bG5lcmFiaWxpdHkgdHlwZXMsIHN1Y2ggYXMgd2VhayBwYXNzd29yZHMsIHVucGF0Y2hlZCBzb2Z0d2FyZSwgYW5kIHNvY2lhbCBlbmdpbmVlcmluZy4gSG93ZXZlciwgZm9yIHplcm8tZGF5IHRoZXJlIGlzIGFuIGluY3JlYXNlIGluIHJlc29sdXRpb24gdGltZSwgc2hvd2luZyBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHplcm8tZGF5IGFuZCByZXNvbHV0aW9uIHRpbWUuICd9CgpnZ3Bsb3QoY3liZXIsIGFlcyh4ID0gY3liZXIkWWVhciwgeSA9IGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuLCBjb2xvciA9IGN5YmVyJFNlY3VyaXR5LlZ1bG5lcmFiaWxpdHkuVHlwZSkpICsKZ2VvbV9zbW9vdGgoYWVzKHggPSBjeWJlciRZZWFyLCB5ID0gY3liZXIkSW5jaWRlbnQuUmVzb2x1dGlvbi5UaW1lLi5pbi5Ib3Vycy4sIGNvbG9yID0gY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlKSwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEwKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJQbG90IG9mIFNlY3VyaXR5IFZ1bG5lcmFiaWxpdHksIFllYXIgb2YgQXR0YWNrLCBhbmQgUmVzb2x1dGlvbiBUaW1lIiwKICAgIHggPSAiWWVhciBvZiBBdHRhY2siLAogICAgeSA9ICJJbmNpZGVudCBSZXNvbHV0aW9uIFRpbWUgaW4gSG91cnMiLAogICAgY29sb3IgPSAiU2VjdXJpdHkgVnVsZXJuYWJpbGl0eSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGlzIHBsb3QgZm9jdXNlcyBvbiB6ZXJvLWRheSB2dWxuZXJhYmlsaXR5IGFuZCBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiB0aHJlYXQgdHlwZXMgaW4gZWFjaCBpbmR1c3RyeS4gVGhlIHgtYXhpcyBzaG93cyBlYWNoIHRocmVhdCB0eXBlIGFuZCB0aGUgeS1heGlzIHNob3dzIHRoZSBwcm9wb3J0aW9uIG9mIGF0dGFja3Mgd2l0aGluIHRoZSBkYXRhc2V0IHRoYXQgZmFsbCBpbnRvIGVhY2ggY2F0ZWdvcnkuIFRoZSBkaWZmZXJlbnQgY29sb3JzIGFyZSBtZWFudCB0byBzaG93IGEgc3ViY2F0ZWdvcnksIHRhcmdldCBpbmR1c3RyeS4gVGhpcyBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiB0aGUgZGF0YXNldCB0aGF0IGZhbGxzIHdpdGhpbiBlYWNoIGNhdGVnb3J5IG9mIHRocmVhdCB0eXBlIGluIGVhY2ggdGFyZ2V0IGluZHVzdHJ5Lid9CiNwbG90IG9mIHplcm8tZGF5IGF0dGFjayB0eXBlIGJ5IHRhcmdldCBpbmR1c3RyeQp6ZXJvX2RhdGEgPC0gY3liZXIgJT4lCiAgZmlsdGVyKFNlY3VyaXR5LlZ1bG5lcmFiaWxpdHkuVHlwZSA9PSAiWmVyby1kYXkiKSAKYSA8LSBnZ3Bsb3QoemVyb19kYXRhLCBhZXMoeCA9IHplcm9fZGF0YSRBdHRhY2suVHlwZSwgZmlsbCA9emVyb19kYXRhJFRhcmdldC5JbmR1c3RyeSApKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicygKICAgIHggPSAiVGhyZWF0IFR5cGUiLAogICAgeSA9ICJQcm9wb3J0aW9uIiwKICAgIGZpbGwgPSAiVGFyZ2V0IEluZHVzdHJ5IiwKICAgIHRpdGxlID0gIlplcm8tZGF5IEluY2lkZW5jZXMgYnkgVGhyZWF0IFR5cGUgdnMuIFRhcmdldCBJbmR1c3RyeSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKZ2dwbG90bHkoYSkKYGBgCgpaZXJvLWRheSBhdHRhY2tzIHNlZW0gdG8gY29tbW9ubHkgdXNlIFBoaXNpbmcgd2l0aGluIGJvdGggcmV0YWlsIGFuZCBJVCBpbmR1c3RyaWVzLCBiYXNlZCBvbiB0aGUgaGVpZ2h0IGFuZCBjb3VudCBvZiB0aGUgYmFyIGluIHRoZXNlIHR3byBjYXRlZ29yaWVzLiBSYW5zb213YXJlIGlzIHRoZSBtb3N0IG1pbmltYWxseSB1c2VkIHplcm8tZGF5IGF0dGFjayBvbiB0aGUgZ292ZXJubWVudCwgd2l0aCBhIGNvdW50IG9mIGp1c3QgMTIuCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoZSBibG94cGxvdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGVmZW5zZSBtZWNoYW5pc20gYW5kIHJlc29sdXRpb24gdGltZSBpbiBob3Vycy4nfQoKY29sMD1jKCIjNDY4MkI0IiwgIiNCNDQ2NEIiLCIjOTkwMEZGIiwiI0ZGNjZGRiIsICIjMDA5OTMzIikKYm94cGxvdChJbmNpZGVudC5SZXNvbHV0aW9uLlRpbWUuLmluLkhvdXJzLiB+IERlZmVuc2UuTWVjaGFuaXNtLlVzZWQsCiAgICAgICAgZGF0YT1jeWJlciwKICAgICAgICB4bGFiPSJEZWZlbnNlIE1lY2hhbmlzbSIsCiAgICAgICAgeWxhYj0iUmVzb2x1dGlvbiBUaW1lIChob3VycykiLAogICAgICAgIGNvbCA9IGNvbDAsCiAgICAgICAgbWFpbj0iSW5jaWRlbnQgUmVzb2x1dGlvbiBUaW1lIGJ5IERlZmVuc2UgTWVjaGFuaXNtIiwKICAgICAgICBjZXgubWFpbiA9IDAuOSwKICAgICAgICBjb2wubWFpbiA9ICJibHVlIikKCgpgYGAKClRoZSBib3hwbG90IHNob3dzIHRoYXQgdGhlIGF2ZXJhZ2UgcmVzb2x1dGlvbiB0aW1lIGFtb25nIGF0dGFja3MgYmFzZWQgb24gZGVmZW5zZSBtZWNoYW5pc21zIGlzIHJlbGF0aXZlbHkgdGhlIHNhbWUuIFRoZSBvbmx5IGV4Y2VwdGlvbiBzZWVtcyB0byBiZSBmaXJld2FsbCwgd2hpY2ggYXBwZWFycyB0byB0YWtlIHNsaWdodGx5IGxlc3MgcmVzb2x1dGlvbiB0aW1lIG9uIGF2ZXJhZ2UuV2UgY2FuIGluZGljYXRlIHRoZSBhdmVyYWdlIGJ5IGxvb2tpbmcgdGhlIGJsYWNrIGhvcml6b250YWwgbGluZSB3aXRoaW4gZWFjaCBib3guIFRoZXkgYWxsIGFwcGVhciB0byBsaW5lIHVwIHdpdGggb25lIGFub3RoZXIgaW5kaWNhdGluZyBzaW1pbGFyIGF2ZXJhZ2VzLgoKIyMgU2tld25lc3MKCmBgYHtyfQoKY3liZXIkQ291bnRyeVtjeWJlciRDb3VudHJ5PT0iVVNBIl08LTEKY3liZXIkQ291bnRyeTwtIGlmZWxzZShjeWJlciRDb3VudHJ5IT0iVVNBIiwwKQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGVbY3liZXIkU2VjdXJpdHkuVnVsbmVyYWJpbGl0eS5UeXBlPT0iWmVyby1kYXkiXTwtMQpjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGU8LWlmZWxzZShjeWJlciRTZWN1cml0eS5WdWxuZXJhYmlsaXR5LlR5cGUhPSJaZXJvLWRheSIsMCkKY3liZXIkTnVtYmVyLm9mLkFmZmVjdGVkLlVzZXJzPC1hcy5udW1lcmljKGN5YmVyJE51bWJlci5vZi5BZmZlY3RlZC5Vc2VycykKY3liZXIkWWVhcjwtYXMubnVtZXJpYyhhcy5mYWN0b3IoY3liZXIkWWVhcikpCmN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuPC1hcy5udW1lcmljKGN5YmVyJEluY2lkZW50LlJlc29sdXRpb24uVGltZS4uaW4uSG91cnMuKQoKCmM8LXNrZXduZXNzKGN5YmVyJEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4uKQpkPC1za2V3bmVzcyhjeWJlciROdW1iZXIub2YuQWZmZWN0ZWQuVXNlcnMpCmU8LXNrZXduZXNzKGN5YmVyJFllYXIpIAoKU2tld25lc3MgPSBjYmluZChGaW5hbmNpYWwuTG9zcyA9IGMsIAogICAgICAgICAgICAgICAgIEFmZmVjdGVkLlVzZXJzID0gIGQsIAogICAgICAgICAgICAgICAgIFllYXIgPSBlKQoKcGFuZGVyKFNrZXduZXNzKQpgYGAKClRoZSBkYXRhIGlzIG5vcm1hbGx5IHNrZXdlZCBhbmQgbm8gdHJhbnNmb3JtYXRpb24gaXMgbmVjZXNzYXJ5LgoKIyBSZWd1bGFyaXplZCBSZWdyZXNzaW9uIE1vZGVsaW5nCgpGb3IgdGhpcyBhbmFseXNpcywgd2Ugd2lsbCBleHBsb3JlIHRocmVlIHR5cGVzIG9mIHJlZ3VsYXJpemVkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsaW5nIHRvIGZpbmQga2V5IHByZWRpY3RvcnMgZm9yIGZpbmFuY2lhbCBsb3NzIGZyb20gY3liZXIgc2VjdXJpdHkgYXR0YWNrcy4KCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuY2FwPSdUaGUgeC1heGlzIG9mIHRoZSBjb2VmZmljaWVudCBwYXRoIGFuYWx5c2lzIHNob3dzIHRoZSBsb2cgb2YgbGFtYmRhLiBBIGxvdyBsYW1iZGEgaGFzIGxlc3MgcmVndWxhcml6YXRpb24gYW5kIGlzIGNsb3NlciB0byBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzLiBBIGhpZ2ggbGFtYmRhIGluZGljYXRlcyBoaWdoZXIgcmVndWxhcml6YXRpb24gYW5kIGF0IHRoaXMgbGFtYmRhIG1hbnkgY29lZmZpY2llbnRzIHdpbGwgc2hyaW5rIHRvIHplcm8uIFRoZSBZLWF4aXMgc2hvd3MgdGhlIGNvZWZmaWNpZW50IHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSBwcmVkaWN0b3IgZm9yIGZpbmFuY2lhbCBsb3NzLiBUaGUgbGluZXMgc2hvd3MgdGhlIHBhdGggb2YgZWFjaCBjb2VmZmljaWVudCBoYXMgbGFtYmRhIGRpZmZlcnMuIFRoZSBkYXNoZWQgbGluZSBzaG93cyB0aGUgb3B0aW1hbCBsZXZlbCBvZiBzaHJpbmthZ2UuIENvZWZmaWNpZW50cyB0byB0aGUgcmlnaHQgb2YgdGhpcyBsaW5lIGFyZSBtb3JlIGZvcmNlZCB0b3dhcmRzIHplcm8uIEF0IHRoaXMgY2hvc2VuIGxhbWJkYSBvbmx5IGNlcnRhaW4gdmFyaWFibGVzIHdpbGwgYmUgbm9uLXplcm8gYW5kIGFyZSBkZWVtZWQgc2lnbmlmaWNhbnQuICd9CgoKCgojIFByZWRpY3RvcnMgKHgpIGFuZCBSZXNwb25zZSBWYXJpYWJsZSAoeSkKCnNldC5zZWVkKDEyMzQ1KQpYIDwtIGFzLmRhdGEuZnJhbWUoY3liZXJbLCAtNV0pICAKCnkgPC0gY3liZXIkRmluYW5jaWFsLkxvc3MuLmluLk1pbGxpb24uLi4KCgp0cmFpbl9pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHksIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKWF90cmFpbiA8LSBtYWtlWChYW3RyYWluX2luZGV4LCBdKQpYX3Rlc3QgPC0gbWFrZVgoWFstdHJhaW5faW5kZXgsIF0pCnlfdHJhaW4gPC0geVt0cmFpbl9pbmRleF0KeV90ZXN0IDwtIHlbLXRyYWluX2luZGV4XQoKI3N0YW5kYXJkaXppbmcgdGhlIGRhdGEKcHJlcHJvY2Vzc19wYXJhbXMgPC0gcHJlUHJvY2VzcyhYX3RyYWluLCBtZXRob2QgPSBjKCJjZW50ZXIiLCJzY2FsZSIpKQpYX3RyYWluIDwtIHByZWRpY3QocHJlcHJvY2Vzc19wYXJhbXMsIFhfdHJhaW4pClhfdGVzdCA8LSBwcmVkaWN0KHByZXByb2Nlc3NfcGFyYW1zLCBYX3Rlc3QpCgoKI2ZpdHRpbmcgdGhlIG1vZGVsCmZpdF9sYXNzbyA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxKSAgIyBMYXNzbwpmaXRfcmlkZ2UgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMCkgICMgUmlkZ2UKZml0X2VsYXN0aWNfbmV0IDwtIGdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSkgICMgRWxhc3RpYyBOZXQKI2Nyb3NzLXZhbGlkYXRpb24KY3ZfbGFzc28gPC0gY3YuZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMSkgICAgICAgIyBMYXNzbyBDVgpjdl9yaWRnZSA8LSBjdi5nbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwKSAgICAgICAjIFJpZGdlIENWCmN2X2VsYXN0aWNfbmV0IDwtIGN2LmdsbW5ldChYX3RyYWluLCB5X3RyYWluLCBhbHBoYSA9IDAuNSkgICMgRWxhc3RpYyBOZXQgQ1YKCiNwbG90IGNvZWZmaWNpZW50IHBhdGgKcGFyKG1hcj1jKDUsNCw2LDMpKQojIFBsb3QgY29lZmZpY2llbnQgcGF0aApwbG90KGZpdF9sYXNzbywgeHZhciA9ICJsYW1iZGEiLCBsYWJlbCA9IFRSVUUsCiAgICAgbHdkID0gMS41LAogICAgIG1haW4gPSAiQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpczogTEFTU08iLAogICAgIGNleC5tYWluID0gMC45LAogICAgIGNvbCA9IHJhaW5ib3coMTApKQphYmxpbmUodiA9IGxvZygxKSwgY29sID0gInB1cnBsZSIsIGx0eSA9IDQsIGx3ZCA9IDIpCmFibGluZSh2ID0gLTEsIGNvbCA9ICJzdGVlbGJsdWUiLCBsdHkgPSAyLCBsd2QgPSAyKQoKYGBgCgpgYGB7cn0KCnBhbmRlcihkYXRhLmZyYW1lKGNvbG5hbWVzKFhfdHJhaW4pW2MoMjcsIDUsIDIwLCAxMildKSxjYXB0aW9uPSAiRmVhdHVyZSBWYXJpYWJsZXMgYXQgbG9nKExhbWJkYSkgPSAwIikKCnBhbmRlcihkYXRhLmZyYW1lKGNvbG5hbWVzKFhfdHJhaW4pW2MoMTgsIDEyLCAxMCwgMjQsIDUsIDIzLCAxNywgMTEsIDMpXSksY2FwdGlvbj0gIiBGZWF0dXJlIFZhcmlhYmxlcyBhdCBsb2coTGFtYmRhKSA9IC0xIikKYGBgCgpUaGUgdGFibGUgYWJvdmUgZGlzcGxheXMgdGhlIGZlYXR1cmUgdmFyaWFibGVzIG9ic2VydmVkIGluIHRoZSBDb2VmZmljaWVudCBBbmFseXNpcyBwbG90LgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgcGxvdCBvZiBwZXJmb3JtYW5jZSBzaG93cyB0aGF0IGFzIGxhbWJkYSBpbmNyZWFzZXMsIHRoZSBNU0UgZGVjcmVhc2VzLiBUaGUgdHdvIHZlcnRpY2FsIGxpbmVzIGdpdmUgYSByZWZlcmVuY2Ugb2YgbGFtYmRhLiAnfQpwYXIobWFyPWMoNSw0LDYsMykpCiMjCnBsb3QoY3ZfbGFzc28sIG1haW4gPSAiUk1TRSBQbG90OiBMQVNTTyIsCiAgICAgY2V4Lm1haW4gPSAwLjkpCmBgYAoKYGBge3J9CiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSBiZXN0IGxhbWJkYQpiZXN0Lmxhc3NvLmxhbWJkYSA8LSBjdl9sYXNzbyRsYW1iZGEubWluCmJlc3QucmlkZ2UubGFtYmRhIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4KYmVzdC5lbGFzdGljLm5ldC5sYW1iZGEgPC0gY3ZfZWxhc3RpY19uZXQkbGFtYmRhLm1pbgojIwojIExhc3NvIFJlZ3Jlc3Npb24gKEwxIFJlZ3VsYXJpemF0aW9uKTogCiMgQ0FVVElPTjogbW9kZWwgZm9ybXVsYSBkaWZmZXJzIGZyb20gdGhlIHJlZ3VsYXIgcmVncmVzc2lvbiBmb3JtdWxhIApsYXNzb19tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIAogICAgICAgICAgICAgICAgICAgICAgeV90cmFpbiwgCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsICAgICAgIyBsYXNzbyByZWdyZXNzaW9uIAogICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gYmVzdC5sYXNzby5sYW1iZGEsc3RhbmRhcmRpemUgPSBUUlVFKSAgICMgdXNlc2VyIHNlbGVjdGVkIGFscGhhLCBvcHRpbWFsIGxhbWJkYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNhbiBiZSBvYnRhaW5lZCB0aHJvdWdoIENWIChzZWUgYmVsb3cpCmxhc3NvX3ByZWRpY3Rpb25zLm9wdCA8LSBwcmVkaWN0KGxhc3NvX21vZGVsLm9wdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcyA9IGJlc3QubGFzc28ubGFtYmRhLCAjIHVzZXIgc2VsZWN0ZWQgbGFtYmRhIHZhbHVlIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKHJlZ3VsYXJpemF0aW9uIHBhcmVtZXRlcikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXd4ID0gWF90ZXN0KSAgIyB0ZXN0IGRhdGEgc2V0IAojIFRoZSBmb2xsb3dpbmcgUk1TRSBvZiBwcmVkaWN0aW9uIHNlcnZlcyBhcyBhIHZhbGlkYXRpb24gLSBvbmUgc3RlcCB2YWxpZGF0aW9uCmxhc3NvX3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gbGFzc29fcHJlZGljdGlvbnMub3B0KV4yKSkgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgUmlkZ2UgUmVncmVzc2lvbiAoTDIgUmVndWxhcml6YXRpb24pCnJpZGdlX21vZGVsLm9wdCA8LSBnbG1uZXQoWF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwLCBsYW1iZGEgPSBiZXN0LnJpZGdlLmxhbWJkYSxzdGFuZGFyZGl6ZT1UUlVFKQpyaWRnZV9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChyaWRnZV9tb2RlbC5vcHQsIHMgPSBiZXN0LnJpZGdlLmxhbWJkYSwgbmV3eCA9IFhfdGVzdCkKcmlkZ2Vfcm1zZS5vcHQgPC0gc3FydChtZWFuKCh5X3Rlc3QgLSByaWRnZV9wcmVkaWN0aW9ucy5vcHQpXjIpKQoKIyBFbGFzdGljIE5ldCAoQ29tYmluYXRpb24gb2YgTDEgYW5kIEwyKQplbGFzdGljX25ldF9tb2RlbC5vcHQgPC0gZ2xtbmV0KFhfdHJhaW4sIHlfdHJhaW4sIGFscGhhID0gMC41LCBsYW1iZGEgPSAwLjEsc3RhbmRhcmRpemUgPSBUUlVFKQplbGFzdGljX25ldF9wcmVkaWN0aW9ucy5vcHQgPC0gcHJlZGljdChlbGFzdGljX25ldF9tb2RlbC5vcHQsIHMgPSAwLjEsIG5ld3ggPSBYX3Rlc3QpCmVsYXN0aWNfbmV0X3Jtc2Uub3B0IDwtIHNxcnQobWVhbigoeV90ZXN0IC0gZWxhc3RpY19uZXRfcHJlZGljdGlvbnMub3B0KV4yKSkKClJNU0Uub3B0ID0gY2JpbmQoTEFTU08ub3B0ID0gbGFzc29fcm1zZS5vcHQsIAogICAgICAgICAgICAgICAgIFJpZGdlLm9wdCA9ICByaWRnZV9ybXNlLm9wdCwgCiAgICAgICAgICAgICAgICAgRWxhc3RpY25ldC5vcHQgPSBlbGFzdGljX25ldF9ybXNlLm9wdCkKCnBhbmRlcihSTVNFLm9wdCkKYGBgCgpUaGUgb3B0aW1hbCBsYW1iZGEgZm9yIGVhY2ggdHlwZSBvZiBsaW5lYXIgcmVncmVzc2lvbiBpcyBzaG93biBhYm92ZS4KCiMjIExBU1NPIFJlZ3Jlc3Npb24gRXF1YXRpb24KCk5vdywgYSBmaW5hbCBtb2RlbCB3aWxsIGJlIGV4dHJhY3RlZCB1c2luZyBMQVNTTywgUmlkZ2UsIGFuZCBFbGFzdGljIE5ldC4KCmBgYHtyfQojTGFzc28KIyBFeHRyYWN0IGNvZWZmaWNpZW50cyB3aXRoIG5hbWVzIGZyb20gZ2xtbmV0CmNvZWZfbGFzc28gPC0gY29lZihjdl9sYXNzbywgcyA9IGJlc3QubGFzc28ubGFtYmRhKQpjb2VmX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGNvZWZfbGFzc28pKQpjb2VmX2RmJGZlYXR1cmUgPC0gcm93bmFtZXMoY29lZl9kZikKY29sbmFtZXMoY29lZl9kZilbMV0gPC0gImNvZWZmaWNpZW50IgoKIyBGaWx0ZXIgbm9uLXplcm8gY29lZmZpY2llbnRzCm5vbnplcm9fY29lZnMgPC0gc3Vic2V0KGNvZWZfZGYsIGNvZWZmaWNpZW50ICE9IDApCgojIFNlcGFyYXRlIGludGVyY2VwdAppbnRlcmNlcHQgPC0gcm91bmQobm9uemVyb19jb2VmcyRjb2VmZmljaWVudFtub256ZXJvX2NvZWZzJGZlYXR1cmUgPT0gIihJbnRlcmNlcHQpIl0sIDQpCm5vbnplcm9fdGVybXMgPC0gc3Vic2V0KG5vbnplcm9fY29lZnMsIGZlYXR1cmUgIT0gIihJbnRlcmNlcHQpIikKCiMgQnVpbGQgbW9kZWwgZXF1YXRpb24gc3RyaW5nCmVxdWF0aW9uIDwtIHBhc3RlMChyb3VuZChub256ZXJvX3Rlcm1zJGNvZWZmaWNpZW50LCA0KSwgIioiLCBub256ZXJvX3Rlcm1zJGZlYXR1cmUpCmNhdCgiTW9kZWwgZXF1YXRpb246IHkgPSIsIGludGVyY2VwdCwgIisiLCBwYXN0ZShlcXVhdGlvbiwgY29sbGFwc2UgPSAiICsgIiksICJcbiIpCgoKYGBgCgojIyBSaWRnZSBSZWdyZXNzaW9uIEVxdWF0aW9uCgpgYGB7cn0KCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgd2l0aCBuYW1lcyBmcm9tIGdsbW5ldApjb2VmX3JpZGdlIDwtIGNvZWYoY3ZfcmlkZ2UsIHMgPSBiZXN0LnJpZGdlLmxhbWJkYSkKY29lZl9kZiA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChjb2VmX3JpZGdlKSkKY29lZl9kZiRmZWF0dXJlIDwtIHJvd25hbWVzKGNvZWZfZGYpCmNvbG5hbWVzKGNvZWZfZGYpWzFdIDwtICJjb2VmZmljaWVudCIKCiMgRmlsdGVyIG5vbi16ZXJvIGNvZWZmaWNpZW50cwpub256ZXJvX2NvZWZzIDwtIHN1YnNldChjb2VmX2RmLCBjb2VmZmljaWVudCAhPSAwKQoKIyBTZXBhcmF0ZSBpbnRlcmNlcHQKaW50ZXJjZXB0IDwtIHJvdW5kKG5vbnplcm9fY29lZnMkY29lZmZpY2llbnRbbm9uemVyb19jb2VmcyRmZWF0dXJlID09ICIoSW50ZXJjZXB0KSJdLCA0KQpub256ZXJvX3Rlcm1zIDwtIHN1YnNldChub256ZXJvX2NvZWZzLCBmZWF0dXJlICE9ICIoSW50ZXJjZXB0KSIpCgojIEJ1aWxkIG1vZGVsIGVxdWF0aW9uIHN0cmluZwplcXVhdGlvbiA8LSBwYXN0ZTAocm91bmQobm9uemVyb190ZXJtcyRjb2VmZmljaWVudCwgNCksICIqIiwgbm9uemVyb190ZXJtcyRmZWF0dXJlKQpjYXQoIk1vZGVsIGVxdWF0aW9uOiB5ID0iLCBpbnRlcmNlcHQsICIrIiwgcGFzdGUoZXF1YXRpb24sIGNvbGxhcHNlID0gIiArICIpLCAiXG4iKQoKCgpgYGAKCiMjIEVsYXN0aWMgTmV0IFJlZ3Jlc3Npb24gRXF1YXRpb24KCmBgYHtyfQoKIyBFeHRyYWN0IGNvZWZmaWNpZW50cyB3aXRoIG5hbWVzIGZyb20gZ2xtbmV0CmNvZWZfZWxhc3RpYyA8LSBjb2VmKGN2X2VsYXN0aWNfbmV0LCBzID0gYmVzdC5lbGFzdGljLm5ldC5sYW1iZGEpCmNvZWZfZGYgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY29lZl9lbGFzdGljKSkKY29lZl9kZiRmZWF0dXJlIDwtIHJvd25hbWVzKGNvZWZfZGYpCmNvbG5hbWVzKGNvZWZfZGYpWzFdIDwtICJjb2VmZmljaWVudCIKCiMgRmlsdGVyIG5vbi16ZXJvIGNvZWZmaWNpZW50cwpub256ZXJvX2NvZWZzIDwtIHN1YnNldChjb2VmX2RmLCBjb2VmZmljaWVudCAhPSAwKQoKIyBTZXBhcmF0ZSBpbnRlcmNlcHQKaW50ZXJjZXB0IDwtIHJvdW5kKG5vbnplcm9fY29lZnMkY29lZmZpY2llbnRbbm9uemVyb19jb2VmcyRmZWF0dXJlID09ICIoSW50ZXJjZXB0KSJdLCA0KQpub256ZXJvX3Rlcm1zIDwtIHN1YnNldChub256ZXJvX2NvZWZzLCBmZWF0dXJlICE9ICIoSW50ZXJjZXB0KSIpCgojIEJ1aWxkIG1vZGVsIGVxdWF0aW9uIHN0cmluZwplcXVhdGlvbiA8LSBwYXN0ZTAocm91bmQobm9uemVyb190ZXJtcyRjb2VmZmljaWVudCwgNCksICIqIiwgbm9uemVyb190ZXJtcyRmZWF0dXJlKQpjYXQoIk1vZGVsIGVxdWF0aW9uOiB5ID0iLCBpbnRlcmNlcHQsICIrIiwgcGFzdGUoZXF1YXRpb24sIGNvbGxhcHNlID0gIiArICIpLCAiXG4iKQoKCgpgYGAKCmBgYHtyfQpsYXNzby5lcnJvciA8LSBtaW4oY3ZfbGFzc28kY3ZtKQpyaWRnZS5lcnJvciA8LSBtaW4oY3ZfcmlkZ2UkY3ZtKQplbmV0LmVycm9yICA8LSBtaW4oY3ZfZWxhc3RpY19uZXQkY3ZtKQoKbW9kZWwuZXJyb3JzIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWwgPSBjKCJMQVNTTyIsICJSaWRnZSIsICJFbGFzdGljIE5ldCIpLAogIENWX0Vycm9yID0gYyhsYXNzby5lcnJvciwgcmlkZ2UuZXJyb3IsIGVuZXQuZXJyb3IpCikKCnBhbmRlcihtb2RlbC5lcnJvcnMpCgpgYGAKCiMjIENvbmNsdXNpb24gZm9yIFJlZ3VsYXJpemVkIFJlZ3Jlc3Npb24gTW9kZWxpbmcKClRoZSB0YWJsZSBhYm92ZSBzaG93cyB0aGUgTVNFIHZhbHVlcyBvciBjcm9zcy12YWxpZGF0ZWQgZXJyb3JzIGZvciBvdXIgdGhyZWUgbW9kZWxzLiBBIHNtYWxsZXIgdmFsdWUgaW5kaWNhdGVzIGEgbW9kZWwgd2l0aCBncmVhdGVyIHBlcmZvcm1hbmNlLiBUaGUgTEFTU08gbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgb3V0IHBlcmZvcm1zIGJvdGggdGhlIFJpZGdlIGFuZCBFbGFzdGljIE5ldCBtb2RlbC4gV2hlbiBwcmVkaWN0aW5nIGZpbmFuY2lhbCBsb3NzIGNhdXNlZCBieSBjeWJlciBzZWN1cml0eSB0aHJlYXRzLCB0aGUgTEFTU08gbGluZXIgcmVncmVzc2lvbiBtb2RlbCB3aWxsIGJlIHRoZSBtb3N0IGVmZmljaWVudC4KCiQkCgpcdGV4dHtGaW5hbmNpYWwgTG9zcyBpbiBNaWxsaW9uc30gPSA1MC41MzY5ICsgNC44MThcdGltZXMgXHRleHR7QXR0YWNrIFR5cGUgRERvU30gLSAwLjQxNzFcdGltZXMgXHRleHR7VGFyZ2V0IEluZHVzdHJ5IEVkdWNhdGlvbn0gKyAyLjMyNFx0aW1lcyBcdGV4dHtUYXJnZXQgSW5kdXN0cnkgR292ZXJubWVudH0gLSAwLjM0NjRcdGltZXMgXHRleHR7VGFyZ2V0IEluZHVzdHJ5IEhlYWx0aGNhcmV9ICsgMC4yNjVcdGltZXMgXHRleHQge0F0dGFjayBTb3VyY2UgSGFja2VyIEdyb3VwfSAtIDAuNTMzNVx0aW1lcyBcdGV4dHtBdHRhY2sgU291cmNlIEluc2lkZXJ9ICsgMC4wMDY1XHRpbWVzIFx0ZXh0IHtEZWZlbnNlIE1lY2hhbmlzbSBVc2VkIEFudGl2aXJ1c30KCiQkCgojIFN1cHBvcnQgVmVjdG9yIFJlZ3Jlc3Npb24KCkZvciB0aGlzIHNlY3Rpb24gb2YgdGhlIGFuYWx5c2lzLCBsaW5lYXIgYW5kIHJhZGlhbCBiYXNpcyBmdW5jdGlvbnMgd2lsbCBiZSBmaXQgdXNpbmcgc3VwcG9ydCB2ZWN0b3IgcmVncmVzc2lvbiBhbmQgYW4gb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyByZWdyZXNzaW9uIG1vZGVsIHdpbGwgYmUgZml0IHVzaW5nIHN0ZXAtd2lzZSB2YXJpYWJsZSBzZWxlY3Rpb24uCgpgYGB7cn0KCmN5YmVyIDwtIHJlYWQuY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vR2FiYnlLOC9EYXRhc2V0cy9yZWZzL2hlYWRzL21haW4vR2xvYmFsX0N5YmVyc2VjdXJpdHlfVGhyZWF0c18yMDE1LTIwMjQuY3N2JykKCiNPbmUtSG90IEVuY29kaW5nCmR1bW15IDwtIGR1bW15VmFycygifi4iLCBkYXRhPWN5YmVyKQpjeWJlcjI8LWRhdGEuZnJhbWUocHJlZGljdChkdW1teSwgbmV3ZGF0YT1jeWJlcikpCgojIyMjIwojIFNwbGl0IGRhdGEgaW50byBmZWF0dXJlcyAoWCkgYW5kIHRhcmdldCAoeSkKWCA8LSBjeWJlcjJbLCAtMjVdICAjIEFsbCBjb2x1bW5zIGV4Y2VwdCB0aGUgdGFyZ2V0IHZhcmlhYmxlCnkgPC0gY3liZXIyWywgMjVdICAgIyBUYXJnZXQgdmFyaWFibGUgKEZpbmFuY2lhbCBMb3NzIGluIE1pbGxpb25zKQoKIyBjcmVhdGUgZHVtbXkgdmFyaWFibGVzCgoKCiMjIyMjCiMgU3BsaXQgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMKc2V0LnNlZWQoMTIzKQp0cmFpbi5pbmRleCA8LSBzYW1wbGUoMTpucm93KGN5YmVyMiksIDAuOCAqIG5yb3coY3liZXIyKSkKWC50cmFpbiA8LSBYW3RyYWluLmluZGV4LCBdCnkudHJhaW4gPC0geVt0cmFpbi5pbmRleF0KWC50ZXN0IDwtIFhbLXRyYWluLmluZGV4LCBdCnkudGVzdCA8LSB5Wy10cmFpbi5pbmRleF0KCiMjIyMjCmN5YmVyMi50cmFpbiA8LSBhcy5kYXRhLmZyYW1lKGN5YmVyMlt0cmFpbi5pbmRleCwgXSkKY3liZXIyLnRlc3QgPC0gY3liZXIyWy10cmFpbi5pbmRleCwgXQojIyMjIwojIyBTZXQgdXAgY3VzdG9tIGNyb3NzLXZhbGlkYXRpb24gY29udHJvbApteV90dW5lX2NvbnRyb2wgPC0gdHVuZS5jb250cm9sKAogIGNyb3NzID0gNSwgICMgVXNlIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uLCB0aGUgZGVmYXVsdCBpcyAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24KICBucmVwZWF0ID0gMSAjIE51bWJlciBvZiByZXBldGl0aW9ucyAoZm9yIHJlcGVhdGVkIGNyb3NzLXZhbGlkYXRpb24pCikKIyMjIyMKIyBQZXJmb3JtIGdyaWQgc2VhcmNoIGZvciBoeXBlcnBhcmFtZXRlciB0dW5pbmc6IFJCRiBrZXJuZWwgaXMgdXNlZAp0dW5lLlJCRiA8LSB0dW5lKHN2bSwKICAgICAgICAgICAgICAgICB0cmFpbi54ID0gWC50cmFpbiwgCiAgICAgICAgICAgICAgICAgdHJhaW4ueSA9IHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IGxpc3QoZXBzaWxvbiA9IHNlcSgwLjEsIDAuNSwgMC4xKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb3N0ID0gYygxLCAxMCwgMTAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYSA9IGMoMC4wMSwgMC4xLCAxKSksICMgSHlwZXJwYXIgaW4gUkJGCiAgICAgICAgICAgICAgICAgICAgdHVuZWNvbnRyb2wgPSBteV90dW5lX2NvbnRyb2wgICAgIyA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIyMjIwojIERpc3BsYXkgdGhlIGJlc3QgcGFyYW1ldGVycwojcHJpbnQodHVuZS5SQkYkYmVzdC5wYXJhbWV0ZXJzKQojIyMjIwojIFRyYWluIHRoZSBmaW5hbCBtb2RlbCB1c2luZyB0aGUgYmVzdCBwYXJhbWV0ZXJzCmZpbmFsLlJCRjwtIHN2bShYLnRyYWluLCB5LnRyYWluLCAKICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiZXBzLXJlZ3Jlc3Npb24iLCAgIyBVc2UgIm51LXJlZ3Jlc3Npb24iIGZvciBudS1TVlIKICAgICAgICAgICAgICAgICAgIGtlcm5lbCA9ICJyYWRpYWwiLCAKICAgICAgICAgICAgICAgICAgIGVwc2lsb24gPSB0dW5lLlJCRiRiZXN0LnBhcmFtZXRlcnMkZXBzaWxvbiwgCiAgICAgICAgICAgICAgICAgICBjb3N0ID0gdHVuZS5SQkYkYmVzdC5wYXJhbWV0ZXJzJGNvc3QsIAogICAgICAgICAgICAgICAgICAgZ2FtbWEgPSB0dW5lLlJCRiRiZXN0LnBhcmFtZXRlcnMkZ2FtbWEpCiMjIyMjCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBzZXQKcHJlZC5SQkYgPC0gcHJlZGljdChmaW5hbC5SQkYsIFgudGVzdCkKCiMgRXZhbHVhdGUgcGVyZm9ybWFuY2UKbXNlLlJCRiA8LSBtZWFuKCh5LnRlc3QgLSBwcmVkLlJCRileMikgICAgIyBtZWFuIHNxdWFyZSBlcnJvcgptYWUuUkJGIDwtIG1lYW4oYWJzKHkudGVzdCAtIHByZWQuUkJGKSkgICAjIG1lYW4gYWJzb2x1dGUgZXJyb3IKCiMjIyMgbGluZWFyIGtlcm5lbAoKIyBQZXJmb3JtIGdyaWQgc2VhcmNoIGZvciBoeXBlcnBhcmFtZXRlciB0dW5pbmc6IFJCRiBrZXJuZWwgaXMgdXNlZAp0dW5lLmxpbiA8LSB0dW5lKHN2bSwgdHJhaW4ueCA9IFgudHJhaW4sIHRyYWluLnkgPSB5LnRyYWluLCAKICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBsaXN0KGVwc2lsb24gPSBzZXEoMC4xLCAwLjUsIDAuMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29zdCA9IGMoMSwgMTAsIDEwMCkpLCAKICAgICAgICAgICAgICAgICAgICB0dW5lY29udHJvbCA9IG15X3R1bmVfY29udHJvbCAjIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uCiAgICAgICAgICAgICAgICAgICAgKQojIyMjCiMgRGlzcGxheSB0aGUgYmVzdCBwYXJhbWV0ZXJzCiNwcmludCh0dW5lLmxpbiRiZXN0LnBhcmFtZXRlcnMpCiMjIyMjCiMgVHJhaW4gdGhlIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMKZmluYWwubGluPC0gc3ZtKFgudHJhaW4sIHkudHJhaW4sIAogICAgICAgICAgICAgICAgICAgdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsICAjIFVzZSAibnUtcmVncmVzc2lvbiIgZm9yIG51LVNWUgogICAgICAgICAgICAgICAgICAga2VybmVsID0gImxpbmVhciIsIAogICAgICAgICAgICAgICAgICAgZXBzaWxvbiA9IHR1bmUubGluJGJlc3QucGFyYW1ldGVycyRlcHNpbG9uLCAKICAgICAgICAgICAgICAgICAgIGNvc3QgPSB0dW5lLmxpbiRiZXN0LnBhcmFtZXRlcnMkY29zdCkKIyMjIyMKIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldApwcmVkLmxpbiA8LSBwcmVkaWN0KGZpbmFsLmxpbiwgWC50ZXN0KQoKIyBFdmFsdWF0ZSBwZXJmb3JtYW5jZQptc2UubGluIDwtIG1lYW4oKHkudGVzdCAtIHByZWQubGluKV4yKSAgICAjIG1lYW4gc3F1YXJlIGVycm9yCm1hZS5saW4gPC0gbWVhbihhYnMoeS50ZXN0IC0gcHJlZC5saW4pKQoKClJCRiA8LSB0dW5lLlJCRiRiZXN0LnBhcmFtZXRlcnMKTGluZWFyIDwtIHR1bmUubGluJGJlc3QucGFyYW1ldGVycwpwYW5kZXIoUkJGLCBjYXB0aW9uPSdSQkYgQmVzdCBQYXJhbWV0ZXJzJykKcGFuZGVyKExpbmVhciwgY2FwdGlvbj0nTGluZWFyIFNWUiBCZXN0IFBhcmFtZXRlcnMnKQoKYGBgCgpUaGUgdGFibGVzIGFib3ZlIHNob3cgdGhlIGZpbmUgdHVuZWQgYmVzdCBwYXJhbWV0ZXJzIGZvciBib3RoIHN1cHBvcnQgdmVjdG9yIHJlZ3Jlc3Npb24gbW9kZWxzLgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgcGxvdHMgYXJlIGEgZGlhZ25vc3RpYyBwbG90IGZvciB0aGUgbGVhc3Qgc3F1YXJlcyByZWdyZXNzaW9uIG1vZGVsLid9CgojIyBvcmRpbmFyeSBMU0UgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHN0ZXB3aXNlIHZhcmlhYmxlIHNlbGVjdGlvbgpsc2UuZml0IDwtIGxtKEZpbmFuY2lhbC5Mb3NzLi5pbi5NaWxsaW9uLi4ufi4sZGF0YT1jeWJlcjIudHJhaW4pCkFJQy5maXQgPC0gc3RlcEFJQyhsc2UuZml0LGRpcmVjdGlvbj0iYm90aCIsIHRyYWNlID0gRkFMU0UpCnByZWQubHNlIDwtIHByZWRpY3QoQUlDLmZpdCwgWC50ZXN0KQptc2UubHNlIDwtIG1lYW4oKHkudGVzdCAtIHByZWQubHNlKV4yKSAgICAjIG1lYW4gc3F1YXJlIGVycm9yCm1hZS5sc2UgPC0gbWVhbihhYnMoeS50ZXN0IC0gcHJlZC5sc2UpKSAgICMgbWVhbiBhYnNvbHV0ZSBlcnJvcgojIyMKcGFyKG1mcm93PWMoMiwyKSwgbWFyPWMoMiwyLDIsMikpCnBsb3QoQUlDLmZpdCkKCmBgYAoKVGhlIG1vZGVsIGFib3ZlIHNhdGlzZmllcyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMgYW5kIHdpbGwgYmUgdXNlZCB0byBoZWxwIGNvbXBhcmUgcGVyZm9ybWFuY2UuCgojIyBDb21wYXJpbmcgUmVncmVzc2lvbiBNb2RlbHMKCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGxvb2sgYXQgdGhlIHBlcmZvcm1hbmNlIG9mIG91ciBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gbW9kZWwgYWZ0ZXIgc3RlcHdpc2UgdmFyaWFibGUgc2VsZWN0aW9uIHdpdGggb3VyIGxpbmVhciBhbmQgcmFkaWFsIGJhc2lzIGZ1bmN0aW9uIG1vZGVscyBmcm9tIHN1cHBvcnQgdmVjdG9yIHJlZ3Jlc3Npb24uV2Ugd2lsbCBjb21wYXJlIHRoZSBtb2RlbCB1c2luZyBNU0UgYW5kIE1BRSB2YWx1ZXMuCgpgYGB7cn0KIyMjClBlcmZvcm1hbmNlIDwtIGRhdGEuZnJhbWUoUkJGLlNWUj1jKG1zZS5SQkYsIG1hZS5SQkYpLAogICAgICAgICAgICAgICAgICAgICAgICAgIExpbmVhci5TVlIgPSBjKG1zZS5saW4sIG1hZS5saW4pLAogICAgICAgICAgICAgICAgICAgICAgICAgIExTRS5SZWcgPWMobXNlLmxzZSwgbWFlLmxzZSkpCnJvdy5uYW1lcyhQZXJmb3JtYW5jZSkgPC0gYygiTVNFIiwgIk1BRSIpCiMjCnBhbmRlcihQZXJmb3JtYW5jZSkKYGBgCgpUaGUgbGluZWFyIGFuZCBsZWFzdCBzcXVhcmVzIG1vZGVsIHBlcmZvcm1lZCBiZXR0ZXIgdGhhbiB0aGUgcmFkaWFsIGJhc2lzIGZ1bmN0aW9uIG1vZGVsLiBUaGUgdHdvIG1vZGVscyBwZXJmb3JtZWQgc2ltaWxhcmx5LCBidXQgdGhlIGxlYXN0IHNxdWFyZXMgbW9kZWwgcGVyZm9ybWVkIHNsaWdodGx5IGJldHRlciBhbmQgd2lsbCBiZSBjaG9zZW4gYXMgdGhlIGJlc3QgbW9kZWwgZm9yIHRoZSBkYXRhLgoKIyMgQ29uY2x1c2lvbiBmb3IgU3VwcG9ydCBWZWN0b3IgUmVncmVzc2lvbgoKQmFzZWQgb24gb3VyIE1TRSBhbmQgTUFFIHNjb3Jlcywgb3VyIGxlYXN0IHNxdWFyZXMgZXN0aW1hdGlvbiBoYXMgdGhlIGJlc3QgbW9kZWwgYmFzZWQgb24gcGVyZm9ybWFuY2UgaW4gY29tcGFyaXNvbiB0byBzdXBwb3J0IHZlY3RvciByZWdyZXNzaW9uIG9uIGxpbmVhciBhbmQgcmFkaWFsIGJhc2lzIGZ1bmN0aW9uLiBJdCBoYXMgbG93ZXIgTVNFIGFuZCBNQUUgc2NvcmVzIGluZGljYXRpbmcgYSBiZXR0ZXIgZml0IG1vZGVsLiBPdXIgZmluYWwgYmVzdCBtb2RlbCBpczoKCiQkClx0ZXh0e0ZpbmFuY2lhbCBMb3NzIGluIE1pbGxpb25zfSA9IDUxLjI3ICsgNC44MThcdGltZXMgXHRleHR7Q291bnRyeUdlcm1hbnl9IC0gMy41MjVcdGltZXMgXHRleHR7Q291bnRyeUluZGlhfSAtIDIuMzI0XHRpbWVzIFx0ZXh0e0F0dGFja1NvdXJjZUluc2lkZXJ9CiQkCg==